﻿using System;
using System.IO.Ports;
using System.Text;
using System.Threading;
using FEZ_Components;
using GHIElectronics.NETMF.FEZ;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT;
using NetMf.CommonExtensions;

namespace PandaDataLogger
{


  public class Program
  {

    #region Variables
    static int seccounter;
    static int[] savedcounters;
    static long eleduration;
    static long watduration;
    static long gasduration;
    static DateTime elepreviouspulse;
    static DateTime watpreviouspulse;
    static DateTime gaspreviouspulse;
    static DateTime starttime;
		static DateTime now;
    static TimeSpan gasmaxpulse = new TimeSpan(0, 0, 15);
    public static TimeSpan uptime;
    static long tpm = TimeSpan.TicksPerMillisecond;
    static RTC_DS1307 RTC;
    static SerialComm sc;
    static bool statusledstate;
    static OutputPort statusled;
    static bool DebugEnabled = false;
		static Thread ledpulser;
		static Timer sectimer;

    
    #endregion

    public static void Main()
    { 
      // Set local DateTime
      RTC = new RTC_DS1307();
      RTC.SetSQWE(RTC_DS1307.SQWE.Pulse_1Hz);
      //Set the date and time
			DateTime tempdate = RTC.GetDateTime();
			if (tempdate.Year < 2011)
			{
				RTC.SetDateTime(2011, 5, 28, 6, 17, 1, 00);
			}
			Utility.SetLocalTime(RTC.GetDateTime());
      starttime = DateTime.Now;

			sectimer = new Timer(new TimerCallback(rtc_OnInterrupt), null, 1000, 1000);

      // Retrieve counters from RTC's NVRAM
      savedcounters = RTC.ReadData();
      CounterCollection.EDCntr = savedcounters[0];
      CounterCollection.ENCntr = savedcounters[1];
      CounterCollection.GACntr = savedcounters[2];
      CounterCollection.WACntr = savedcounters[3];

      // Initialize Display
      FEZ_Extensions.Large128x64Display.Initialize(FEZ_Pin.Digital.Di9, FEZ_Pin.Digital.Di8, FEZ_Pin.Digital.Di10, SPI.SPI_module.SPI1);
      Screens.SelectScreen(0);

      // Setup Interrupts for Electricity, Gas, Water, RTC
      //InterruptPort up = new InterruptPort((Cpu.Pin)FEZ_Pin.Interrupt.LDR, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeHigh);
      InterruptPort ele = new InterruptPort((Cpu.Pin)FEZ_Pin.Interrupt.Di7, false, Port.ResistorMode.PullDown, Port.InterruptMode.InterruptEdgeHigh);
      InterruptPort wat = new InterruptPort((Cpu.Pin)FEZ_Pin.Interrupt.Di6, false, Port.ResistorMode.PullDown, Port.InterruptMode.InterruptEdgeHigh);
      InterruptPort gas = new InterruptPort((Cpu.Pin)FEZ_Pin.Interrupt.Di5, false, Port.ResistorMode.PullDown, Port.InterruptMode.InterruptEdgeHigh);
      //InterruptPort rtc = new InterruptPort((Cpu.Pin)FEZ_Pin.Interrupt.Di4, false, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeLow);
      statusled = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, false);

			ledpulser = new Thread(ledpulse);
			ledpulser.Start();

      // Attach event handlers to interrupts
      //up.OnInterrupt += new NativeEventHandler(ScreenSelector);
      ele.OnInterrupt += new NativeEventHandler(ele_OnInterrupt);
      wat.OnInterrupt += new NativeEventHandler(wat_OnInterrupt);
      gas.OnInterrupt += new NativeEventHandler(gas_OnInterrupt);
      //rtc.OnInterrupt += new NativeEventHandler(rtc_OnInterrupt);

      sc = new SerialComm();
      sc.Open();

      MessageEventHandler.MessageEvent += new NewMessageEventHandler(MessageEventHandler_MessageEvent);

      // Sleep forever
      Thread.Sleep(Timeout.Infinite);
    }

    /// <summary>
    /// Handle incomming serial messages
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="se"></param>
    static void MessageEventHandler_MessageEvent(object sender, MessageEventArgs se)
    {
      string[] msgpart;
      if (se.LatestMessage.Length > 0)
      { 
      if (se.LatestMessage.IndexOf('|') > 0)
      {
        msgpart = se.LatestMessage.Split('|');
      }
      else
      {
        msgpart = new string[1];
        msgpart[0] = se.LatestMessage;
      }
      switch (msgpart[2])
      {
        case Commands.SetTime:
          SetTime(msgpart);
          sc.Send("DateTime set correctly.");
          break;
        case Commands.SetCounters:
          SetCounters(msgpart);
          sc.Send("Set counters successfully.");
          break;
        case Commands.DateTime:
          sc.Send("dt" + DateTime.Now.ToString());
          break;
        case Commands.Who:
          sc.Send("I'm FEZ Panda, and you must be ...");
          break;
        case Commands.GetCounters:
          sc.Send("PANDA|HAMS|CounterData|9|" + CounterCollection.Message());
          break;
        default:
          sc.Send("Did not understand you.");
          break;
      }
      }
    }

    /// <summary>
    /// Handles the 1Hz pulse from the RTC
    /// 1) Update UpTime
    /// 2) Save every 5 seconds the counters to the RTC's NVRAM
    /// 3) Rotate screens every 10 seconds
    /// 4) Set bool HighRate depending on time of day and day
    /// 5) Update LCD every second
    /// </summary>
    /// <param name="data1"></param>
    /// <param name="data2"></param>
    /// <param name="time"></param>
    //static void rtc_OnInterrupt(uint data1, uint data2, DateTime time)
    static void rtc_OnInterrupt(object o)
    {
			now = DateTime.Now;
      uptime = now.Subtract(starttime);
      seccounter++;

      if (seccounter % 5 == 0)
      { 
        RTC.SaveData(CounterCollection.EDCntr, CounterCollection.ENCntr, CounterCollection.GACntr, CounterCollection.WACntr);
      }

      if (seccounter % 10 == 0)
      {
        CounterCollection.FreeMem = Debug.GC(false);
            Screens.ScreenRotator();
        seccounter = 0;
      }

      CounterCollection.SetERate(now);
      if (now.Subtract(gaspreviouspulse) > gasmaxpulse) CounterCollection.GACons = 0;
      Screens.UpdateScreens(Screens.screennumber);

    }

		static void ledpulse()
		{
			while (true)
			{
				statusled.Write(statusledstate);
				statusledstate = !statusledstate;
				Thread.Sleep(1000);
			}
		}

    /// <summary>
    /// Handles the Water pulse
    /// </summary>
    /// <param name="data1"></param>
    /// <param name="data2"></param>
    /// <param name="time"></param>
    static void wat_OnInterrupt(uint data1, uint data2, DateTime time)
    {
      watduration = (long)time.Subtract(watpreviouspulse).Ticks / tpm; // µs
      watpreviouspulse = time;
      CounterCollection.WACons = watduration;
      CounterCollection.WACntr += 1;
    }

    /// <summary>
    /// Handles the Electricity pulse
    /// </summary>
    /// <param name="data1"></param>
    /// <param name="data2"></param>
    /// <param name="time"></param>
    static void ele_OnInterrupt(uint data1, uint data2, DateTime time)
    {
      eleduration = (long)time.Subtract(elepreviouspulse).Ticks / tpm; // µs
      elepreviouspulse = time;

        CounterCollection.EDCons = eleduration;
      if (CounterCollection.HighRate)
      {
        CounterCollection.EDCntr += 1 ;
      }
      else
      {
        CounterCollection.ENCntr += 1 ;
      }
    }

    /// <summary>
    /// Handles the Gas pulse
    /// </summary>
    /// <param name="data1"></param>
    /// <param name="data2"></param>
    /// <param name="time"></param>
    static void gas_OnInterrupt(uint data1, uint data2, DateTime time)
    {
      gasduration = (long)time.Subtract(gaspreviouspulse).Ticks / tpm; // µs
      gaspreviouspulse = time;
      CounterCollection.GACons = gasduration;
      CounterCollection.GACntr += 1 ;
    }

    /// <summary>
    /// Handles the screen selector
    /// </summary>
    /// <param name="data1"></param>
    /// <param name="data2"></param>
    /// <param name="time"></param>
    static void ScreenSelector(uint data1, uint data2, DateTime time)
    {
    }

    static void SetTime(string[] dts)
    {
      int dtsyear, dtsmonth, dtsday, dtsdow, dtshour, dtsmin, dtssec;
      Parse.TryParseInt(dts[4], out dtsyear);
      Parse.TryParseInt(dts[5], out dtsmonth);
      Parse.TryParseInt(dts[6], out dtsday);
      Parse.TryParseInt(dts[7], out dtsdow);
      Parse.TryParseInt(dts[8], out dtshour);
      Parse.TryParseInt(dts[9], out dtsmin);
      Parse.TryParseInt(dts[10], out dtssec);
     
      RTC.SetDateTime(dtsyear, dtsmonth, dtsday, dtsdow, dtshour, dtsmin, dtssec);
      Utility.SetLocalTime(RTC.GetDateTime());
    }

    static void SetCounters(String[] parms)
    { 
      int EDC, ENC, GC, WC;
      Parse.TryParseInt(parms[4], out EDC);
      Parse.TryParseInt(parms[5], out ENC);
      Parse.TryParseInt(parms[6], out GC);
      Parse.TryParseInt(parms[7], out WC);
      EDC = EDC * (1000 / 100);
      ENC = ENC * (1000 / 100);
      GC = GC * (100 / 100);
      WC = WC * (2000 / 100);

      RTC.SaveData(EDC, ENC, GC, WC);
      savedcounters = RTC.ReadData();
      CounterCollection.EDCntr = savedcounters[0];
      CounterCollection.ENCntr = savedcounters[1];
      CounterCollection.GACntr = savedcounters[2];
      CounterCollection.WACntr = savedcounters[3];
    }

    static void PrintDebug(string msg)
    {
      if (DebugEnabled) Debug.Print(msg);
    }

    private static double intToDouble(int val, int precision)
    {
        double value = (double)val * precision;
        value = System.Math.Round(value);
        value /= (precision * precision);
        return value;
    }

  }
}
